/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Simulation.Services.Common;

import Simulation.Services.Common.Dis.*;
import Simulation.Utils.CoordinateConversions;
import java.net.*;
/**
 *
 * @author CATT
 */

public class DisManager 
{
    public interface Command{
        public void execute(Object data);
    }
    private DisPduFactory pduFactory = new DisPduFactory();
    private String MULTICAST_GROUP;
    private int PORT;
    private MulticastSocket socket;
    private InetAddress address;
    private NetworkInterface nif;        
    private InetAddress localDisNif;
    private int SiteId;
    private int ApplicationId;
    private String ADDR = "192.168.63.9";
    private NetworkEntityType[] NetworkTypesTable = new NetworkEntityType[10];
    private boolean RemoteEntityStoringEnabled = false;
    private Command AddRemoteEntityToDatabase;
    
    public int GetSiteId(){
        return SiteId;
    }

    public int GetApplicationId(){
        return ApplicationId;
    }
    
    private void ProcessPdu(Pdu pduMessage){
        if (pduMessage != null) {
            if(pduMessage instanceof EntityStatePdu){
                EntityStatePdu espdu = (EntityStatePdu)pduMessage;
                if ((AddRemoteEntityToDatabase != null)&&(RemoteEntityStoringEnabled)){
                    AddRemoteEntityToDatabase.execute(espdu);
                }
            }
            else if (pduMessage instanceof DetonationPdu){
                System.out.println("Detontation PDU recieved!");
            }
        }
    }

    public void EnableRemoteEntityStoring(Command cmd)
    {
        AddRemoteEntityToDatabase = cmd;
        RemoteEntityStoringEnabled = true;
    }

    public void ProcessPduMessage(byte[] pduMessages)
    {
        int pktLength = pduMessages.length;
        int currentIndex = 0;
        int bundledPduCount = 0;
        
        //Make sure process all PDU's if there are some bundled together
        while (currentIndex < pktLength)
        {
            bundledPduCount++;

            //Create a byte array large enough to store the current PDU data
            byte[] currentPdu = new byte[pduMessages.length-currentIndex];

            //Copy over the current section of the pdu byte array
            for(int i =0; i< (pduMessages.length-currentIndex);i++){
                currentPdu[i] = pduMessages[currentIndex+i];
            }

            //Attempt to create a PDU from the current PDU data
            Pdu pdu = pduFactory.createPdu(currentPdu);

            //See if we were successful in creating a PDU
            if (pdu != null) {
                //Now process the
                ProcessPdu(pdu);
                //Move the pointer to the start of the next set of data within the array
                currentIndex = currentIndex + pdu.getLength();
            }
            else{
                //Move straight to the end and exit
                currentIndex = pktLength;
            }
        }        
    }
    
    //Establish Dis Network Connection
    public void EstablishConnection(String disAddr, String multCastGrp,int port, int site,int appId) {
        ADDR=disAddr;
        MULTICAST_GROUP=multCastGrp;
        PORT = port;
        SiteId = site;
        ApplicationId = appId;
        
        try{
            socket = new MulticastSocket(PORT);
            address = InetAddress.getByName(MULTICAST_GROUP);
            localDisNif = InetAddress.getByName(ADDR);
            nif = NetworkInterface.getByInetAddress(localDisNif);   
            socket.setNetworkInterface(nif);
        }
        catch(Exception e){
            System.out.println(e);
        }
    }

    public class NetworkEntityType{
        short Pk_EntityType = 0;
        short EntityKind = 0;
        short Domain = 0;
        short Country = 0;
        short Category = 0;
        short Subcategory = 0;
        short Spec = 0;
        short Extra = 0;
    }

    //MAKE CONFIG FILE ITEMSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
    private void InitializeEntityTypes(){
        //Null
        NetworkTypesTable[0] = new NetworkEntityType();
        NetworkTypesTable[0].Pk_EntityType = 0;
        NetworkTypesTable[0].EntityKind = 0;
        NetworkTypesTable[0].Domain = 0;
        NetworkTypesTable[0].Country = 0;
        NetworkTypesTable[0].Category = 0;
        NetworkTypesTable[0].Subcategory = 0;
        NetworkTypesTable[0].Spec = 0;
        NetworkTypesTable[0].Extra = 0;
        
        //Warrior
        NetworkTypesTable[1] = new NetworkEntityType();
        NetworkTypesTable[1].Pk_EntityType = 0;
        NetworkTypesTable[1].EntityKind = 1;
        NetworkTypesTable[1].Domain = 1;
        NetworkTypesTable[1].Country = 224;
        NetworkTypesTable[1].Category = 2;
        NetworkTypesTable[1].Subcategory = 8;
        NetworkTypesTable[1].Spec = 2;
        NetworkTypesTable[1].Extra = 0;
        
        //Challenger
        NetworkTypesTable[2] = new NetworkEntityType();
        NetworkTypesTable[2].Pk_EntityType = 0;
        NetworkTypesTable[2].EntityKind = 1;
        NetworkTypesTable[2].Domain = 1;
        NetworkTypesTable[2].Country = 224;
        NetworkTypesTable[2].Category = 1;
        NetworkTypesTable[2].Subcategory = 2;
        NetworkTypesTable[2].Spec = 2;
        NetworkTypesTable[2].Extra = 3;
        
        //Titan
        NetworkTypesTable[3] = new NetworkEntityType();
        NetworkTypesTable[3].Pk_EntityType = 0;
        NetworkTypesTable[3].EntityKind = 1;
        NetworkTypesTable[3].Domain = 1;
        NetworkTypesTable[3].Country = 224;
        NetworkTypesTable[3].Category = 3;
        NetworkTypesTable[3].Subcategory = 13;
        NetworkTypesTable[3].Spec = 1;
        NetworkTypesTable[3].Extra = 2;
    }
    
    public EntityType GetDisEntityType(int EntityType){

        EntityType entType = new EntityType(); 
        
        entType.setPk_EntityType(NetworkTypesTable[EntityType].Pk_EntityType);
        entType.setEntityKind(NetworkTypesTable[EntityType].EntityKind);
        entType.setDomain(NetworkTypesTable[EntityType].Domain);
        entType.setCountry(NetworkTypesTable[EntityType].Country);
        entType.setCategory(NetworkTypesTable[EntityType].Category);
        entType.setSubcategory(NetworkTypesTable[EntityType].Subcategory); 
        entType.setSpec(NetworkTypesTable[EntityType].Spec);
        entType.setExtra(NetworkTypesTable[EntityType].Extra);

        return entType;
    }  
    
    public void BuildAndSendEntityStatePdu(EntityDatabase.EntityRecord ent){
        BuildAndSendEntityStatePdu(ent.ExerciseId,ent.EntityID.entity,ent.Type,ent.Force,ent.Marking,ent.Location,ent.Orientation);            
    }
    
    public void BuildAndSendEntityStatePdu(int exerciseId, int entityId, int EntityType, int Force, String entityMarking,
                                            Vector3Double entLocation, Vector3Double entOrientation){
        
        EntityStatePdu entStatePdu = new EntityStatePdu();
        entStatePdu.setExerciseID((short)exerciseId);
        
        //First set the entity Id
        EntityID entId = entStatePdu.getEntityID();
        entId.setSite(SiteId);
        entId.setApplication(ApplicationId);
        entId.setEntity(entityId);
        
        //Now set the entity type
        entStatePdu.setEntityType(GetDisEntityType(EntityType));

        //Now the force and appearance
        entStatePdu.setForceId((short)(Force));
        entStatePdu.setEntityAppearance(4194304);
        
        //Set the Location and orientation
        Vector3Double location = entStatePdu.getEntityLocation();
        double[] convertedLoc = CoordinateConversions.getXYZfromLatLonDegrees(entLocation.getX(),entLocation.getY(),entLocation.getZ());
        location.setX(convertedLoc[0]);
        location.setY(convertedLoc[1]);
        location.setZ(convertedLoc[2]);

        Orientation orientation = entStatePdu.getEntityOrientation();
        Vector3Double convertedOrient = CoordinateConversions.getDisOrientationfromOrientation(entOrientation);
        orientation.setTheta((float)convertedOrient.getX());
        orientation.setPsi((float)convertedOrient.getZ());
        orientation.setPhi((float)convertedOrient.getY());
        orientation.setPk_Orientation(0);
        
        //Set the entity marking
        Marking entMrk = new Marking();
        entMrk.setPk_Marking(0);
        entMrk.setCharacterSet((short)1);
        char[] tempCharAry = entityMarking.toCharArray();
        byte[] entMarking = new byte[10];
        
        for (int i = 0;i<entityMarking.length();i++) {
            if (i<=10) {
                entMarking[i] = (byte) tempCharAry[i];
            }
            else {
                break;
            }
        }
        entMrk.setCharacters(entMarking);
        entStatePdu.setMarking(entMrk);
        
        //Now set the pdu misc items
        entStatePdu.setPduLength((short)144);
        entStatePdu.setProtocolVersion((short)5);
        long timestamp = entStatePdu.getTimestamp();
        timestamp++;
        entStatePdu.setTimestamp(timestamp);  
        
        //Now send the Pdu
        byte data[] = entStatePdu.marshal();
        DatagramPacket packet = new DatagramPacket(data, data.length, address, PORT);
        
        try{            
        socket.send(packet);
        }
        catch(Exception e){
            System.out.println("ERROR > DisNetwork.BuildAndSendEntityStatePdu : " + e.getMessage());    
        }        
    }
    
    public DisManager(){
        InitializeEntityTypes();
    }
}
